package org.vacoor.xqq.ui.comp.richeditor;


import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.text.*;
import java.awt.*;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

/**
 * User: vacoor
 */
public class RichEditor extends JScrollPane implements Iterable<RichElement> {
    private JTextPane textPane;
    private MutableAttributeSet PARAGRAPH_END_STYLE;
    private Style defaultStyle = new Style();

    public RichEditor() {
        StyleContext sc = StyleContext.getDefaultStyleContext();
        textPane = new JTextPane();
//        textPane.setMargin(new Insets(10, 10, 10, 10));
        textPane.setOpaque(false);
//        textPane.setDragEnabled(true);
//        textPane.setDropMode(DropMode.USE_SELECTION);
        /*
        textPane.setCaret(new DefaultCaret(){
            @Override
            protected Highlighter.HighlightPainter getSelectionPainter() {
                return new DefaultHighlighter.DefaultHighlightPainter(null) {
                    @Override
                    public Shape paintLayer(Graphics g, int offs0, int offs1, Shape bounds, JTextComponent c, View view) {
                        if(c instanceof JTextPane) {
                            StyledDocument doc = ((JTextPane) c).getStyledDocument();
                            for(int offset = offs0; offset < offs1 + 1; offset++)  {
                                AttributeSet attrSet = doc.getCharacterElement(offset).getAttributes();
                                Icon icon = StyleConstants.getIcon(attrSet);
                                if(icon != null && icon instanceof ImageIcon) {
                                    ImageIcon imageIcon = (ImageIcon) icon;
                                    imageIcon.setImage(NegativeFilter.createNegativeImage(imageIcon.getImage())); //卡
                                }
                            }
                        }
                        return super.paintLayer(g, offs0, offs1, bounds, c, view);    //To change body of overridden methods use File | Settings | File Templates.
                    }
                };
            }
        });
        */

        // 默认样式

        // 段落结束样式
//        StyleContext sc = StyleContext.getDefaultStyleContext();
        javax.swing.text.Style style = sc.getStyle(StyleContext.DEFAULT_STYLE);
        PARAGRAPH_END_STYLE = sc.addStyle("PARAGRAPH_END_STYLE", style);
        StyleConstants.setSpaceBelow(PARAGRAPH_END_STYLE, 10f);

        this.setViewportView(textPane);
        this.setHorizontalScrollBarPolicy(HORIZONTAL_SCROLLBAR_NEVER);
        this.getViewport().setOpaque(false);
        this.setViewportBorder(BorderFactory.createEmptyBorder(2, 3, 5, 3));

        this.setBorder(null);
        this.setMinimumSize(new Dimension(50, 50)); // 设置最小大小, 用于SplitPane
        this.setOpaque(false);

        defaultStyle.addChangeListener(new ChangeListener() {
            @Override
            public void stateChanged(ChangeEvent e) {
                updateStyle();
            }
        });
    }

    public void setDefaultStyle(Style style) {
        this.defaultStyle = style;
        updateStyle();
    }

    public Style getDefaultStyle() {
        return this.defaultStyle;
    }

    protected void updateStyle() {
        javax.swing.text.Style style = textPane.getStyle(StyleContext.DEFAULT_STYLE);
        style.addAttributes(defaultStyle);

        StyledDocument doc = textPane.getStyledDocument();
        doc.setCharacterAttributes(0, doc.getLength(), style, true);

        // 将内容为空时, 设置样式, 在再次输入时显示会丢失问题(换行又正确), 可以通过以下方式修复下
        // 这个就OK, 设置新输入的属性
        textPane.getInputAttributes().addAttributes(defaultStyle);
//        textPane.setCharacterAttributes(style, true);
        /*
        StyledDocument doc = textPane.getStyledDocument();
        doc.setParagraphAttributes(0, doc.getLength(), style, true);
        */
    }

    /**
     * 获取内容长度
     *
     * @return
     */
    public int getContentLength() {
        return textPane.getStyledDocument().getLength();
    }

    /**
     * 获取编辑器中的内容
     *
     * @return
     */
    public List<RichElement> getContent() {
        List<RichElement> content = new ArrayList<RichElement>();

        StyledDocument doc = textPane.getStyledDocument();
        Element root = doc.getDefaultRootElement();

        // 段落元素
        for (int p = 0; p < root.getElementCount(); p++) {
//            doc.getParagraphElement(p);
            Element paragraph = root.getElement(p);

            // 这个等价于isLeaf() 应该不会被执行( root 下为段落(BranchElement) )
            // if( !(paragraph instanceof AbstractDocument.BranchElement) ) { continue; }

            // 遍历段落中每个元素
            for (int e = 0; e < paragraph.getElementCount(); e++) {
                Element elem = paragraph.getElement(e);
                AttributeSet attrSet = elem.getAttributes();

                // if( !elem.isLeaf() ) { continue; } // 这个应该不会执行, 段落下面为元素(LeafElement)

                /*
                Component component = StyleConstants.getComponent(attrSet);
                if (null != component) {
                    System.out.println("优先取component: " + component);
                    continue;
                }
                */

                Icon icon = StyleConstants.getIcon(attrSet);
                if (icon != null) {
                    int id = -1;
                    Object key = attrSet.getAttribute(Face.FACE_ID_ATTR_KEY);
                    if (key != null && key instanceof Number) {
                        id = ((Number) key).intValue();
                    }

                    content.add(new Face(id, icon));
                    continue;
                }

                // 如果都不存在, 则直接使用其文本
                String text = "(该位置部分文字丢失..)";
                int start = elem.getStartOffset();
                int end = elem.getEndOffset();
                try {
                    text = doc.getText(start, end - start);
                } catch (BadLocationException e1) {
//                    throw new RuntimeException(e1);
                }

                if (text.length() > 0) {
                    // 会在长度之后多个 \n
                    if (end == doc.getLength() + 1 && text.endsWith("\n")) {
                        text = text.substring(0, text.length() - 1);
                    }
                    Text textElement = new Text(text);
                    textElement.setStyle(new org.vacoor.xqq.ui.comp.richeditor.Style(attrSet));

                    content.add(textElement);
                }
            }
        }

        return content;
    }

    public void setContent(Iterable<RichElement> elems) {
        clearContent();
        appendElements(elems);
    }

    /**
     * 在当前内容后追加内容
     *
     * @param elems
     */
    public void appendElements(Iterable<RichElement> elems) {
        for (RichElement e : elems) {
            appendElements(e);
        }
    }

    /**
     * 在当前内容后追加内容
     *
     * @param elems
     */
    public void appendElements(RichElement... elems) {
        for (RichElement e : elems) {
            e.appendTo(this);
        }
    }

    /**
     * 在指定位置插入内容
     *
     * @param elem
     * @param offset
     */
    public void insertElement(RichElement elem, int offset) {
        elem.insertTo(this, offset);
    }

    /**
     * 插入一个段落结束
     * 默认会认为一个换行就是一个段落的结束, 这里简单修改以下
     */
    public void appendParagraphEnd() {
        StyledDocument doc = textPane.getStyledDocument();
        try {
            String text = doc.getText(doc.getLength() - 1, 1);
            if (!"\n".equals(text) && !"\r".equals(text)) {
                doc.insertString(doc.getLength(), "\r\n", textPane.getStyle(StyleContext.DEFAULT_STYLE));
            }
        } catch (BadLocationException e) {
        }
        moveCaretPositionToEnd();
        doc.setParagraphAttributes(doc.getLength() - 1, 1, PARAGRAPH_END_STYLE, false);
    }

    /**
     * 将插入符移动到最后
     * 保证视口滚动到最后
     */
    public void moveCaretPositionToEnd() {
        textPane.setCaretPosition(this.getContentLength());
    }

    public void clearContent() {
        textPane.setText("");
    }

    public void setEditable(boolean editable) {
        textPane.setEditable(editable);
    }

    public boolean isEditable() {
        return textPane.isEditable();
    }

    @Override
    public Iterator<RichElement> iterator() {
        return getContent().iterator();
    }

    public JTextPane getTextPane() {
        return this.textPane;
    }
}
